package concurrent.threadscheduling;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程循环执行
 *
 * 需要注意的点：
 * 1）不管多少线程控制，其实只需要一个锁就可以，外加一个全局的 volatile变量标记当前执行到的线程状态
 * 2）一定要在获取锁后，使用 while 对谓词 循环校验
 *
 * @Author Antony
 * @Since 2018/6/20 22:12
 */
public class ThreadLoopExecute {

    public static void main(String[] args) throws InterruptedException {
//        loopPrintABC_MethodA(); //循环打印ABC
//        loopPrintABC_Lock();
        loopPrintABC_sych();
    }



    static volatile String flag = "A";
    static ReentrantLock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();

    /**
     * 使用一把锁显示锁 Lock
     * 通过锁的 await() 和 singal() 通知来调度线程
     * 并且通过一个全局变量 flag 来标记顺序
     */
    private static void loopPrintABC_Lock(){
        Thread t1 = new Thread(new LockPrint("A","B"));
        Thread t2 = new Thread(new LockPrint("B","C"));
        Thread t3 = new Thread(new LockPrint("C","D"));
        Thread t4 = new Thread(new LockPrint("D","A"));


        t3.start();
        t4.start();
        t2.start();
        t1.start();

    }

    private static class LockPrint implements Runnable{
        String value;
        String next;

        public LockPrint(String value, String next) {
            this.value = value;
            this.next = next;
        }

        @Override
        public void run() {
            System.out.println(value + " Thread Start...");
            int count = 10; //循环打印10遍
            while (count >0){
                lock.lock();
                try {
                    while(!value.equals(flag)){
                        condition.await();
                    }
                    System.out.print(value);
                    flag = next;
                    condition.signalAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();  //注意！一定要显示的释放锁
                }

                count--;
            }

        }
    }


    static volatile String sychFlag = "A";
    private static void loopPrintABC_sych(){
        Object lock = new Object();

        Thread t1 = new Thread(new PrintSych(lock,"A","B"));
        Thread t2 = new Thread(new PrintSych(lock,"B","C"));
        Thread t3 = new Thread(new PrintSych(lock,"C","A"));
//        Thread t4 = new Thread(new PrintSych(lock,"D","A"));

//        t4.start();
        t3.start();
        t2.start();
        t1.start();
    }

    private static class PrintSych implements Runnable{
        Object lockObj;
        String value;
        String next;

        public PrintSych(Object lockObj, String value, String next) {
            this.lockObj = lockObj;
            this.value = value;
            this.next = next;
        }

        @Override
        public void run() {
            System.out.println(value + " Thread Start...");
            int count = 5;
            while(count>0){
                synchronized (lockObj){
                    try {
                        while(!value.equals(sychFlag)){     //条件一定要用while反复校验
                            lockObj.wait();
                        }
                        System.out.print(value);
                        sychFlag = next;
                        lockObj.notifyAll();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                count--;
            }

        }
    }


    /**
     * 需要占用两个锁，并且需要线程顺序启动来保证有序
     * @throws InterruptedException
     */
    private static void loopPrintABC_MethodA() throws InterruptedException {
        Object a = new Object();
        Object b = new Object();
        Object c = new Object();

        Thread t1 = new Thread(new Print(a,b, "A"));
        Thread t2 = new Thread(new Print(b,c, "B"));
        Thread t3 = new Thread(new Print(c,a, "C"));

        // 线程先后启动间隔1毫秒，保证启动有序
        t1.start();
        Thread.sleep(1L);

        t2.start();
        Thread.sleep(1L);

        t3.start();
    }

    static class Print implements Runnable{
        private Object current;
        private Object next;
        private String value;

        public Print(Object current, Object next, String value) {
            this.current = current;
            this.next = next;
            this.value = value;
        }

        @Override
        public void run() {
            System.out.println(current + " Thread Start...");
            int count=0;
            while(true){
                synchronized (current){
                    synchronized (next){
                        System.out.print(value);
                        count++;
                        next.notify();
                    }

                    try {
                        //循环打印五遍
                        if(count==5){
                            return;
                        }
                        current.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
